#include <iostream>
#include <string>
#include <cstdlib>
#include <vector>
#include <unistd.h>
#include <ctime>
#include <sys/wait.h>
#include <sys/types.h>
#include "task.hpp"

using namespace std;
//枚举错误码
enum
{
    UsageError = 1,
    ArgError,
    PipeError
};

//运行程序指令输入错误时的提示符
void Usage(const string& proc)
{
    cout << "Usage: " << proc << " subprocess-num" << endl;
}

// 管理子进程的管道信道
class Channel
{
public:
    Channel(int pipefd, pid_t sub_id, const string& name)
        :_pipefd(pipefd), _sub_process_id(sub_id), _name(name)
    { }

    void PrintDebug()
    {
        cout << "_pipefd: " << _pipefd;
        cout << ", _sub_process_id: " << _sub_process_id;
        cout << ", _name: " << _name << endl;
    }

    string name() { return _name; }
    int wfd() { return _pipefd; }
    pid_t pid() { return _sub_process_id; }
    void Close() { close(_pipefd); }
    ~Channel()
    { }
private:
    int _pipefd;
    pid_t _sub_process_id;
    string _name;     
};

// 进程池
class ProcessPool
{
public:
    ProcessPool(int sub_process_num) 
        : _sub_process_num(sub_process_num)
    { }
    int CreateProcess(work_t work) // 回调函数，让子进程开始工作
    {
        //注意：每次创建子进程时会继承父进程连接前面创建子进程管道的写段
        //fds存储每次连接管道的写端，用于后面关闭子进程继承父进程连接管道的写端
        vector<int> fds;
        // 依次创建每个子进程及对应的管道信道，并将其用管道信道channels管理起来
        for (int number = 0; number < _sub_process_num; number++)
        {
            int pipefd[2]{0};
            int n = pipe(pipefd);
            if (n < 0)
            {
                return PipeError;
            }
            pid_t id = fork();
            // child -> read。由于继承父进程文件描述符的关系，下面创建的所有子进程的读端全部是3
            if (id == 0)
            {
                if (!fds.empty())
                {
                    cout << "close w fd: ";
                    for (auto fd : fds)
                    {
                        close(fd);
                        cout << fd << " ";
                    }
                    cout << endl;
                }
                sleep(1);
                close(pipefd[1]);
                // 执行任务
                dup2(pipefd[0], 0); // 标准输入从管道中读取，即重定向管道文件
                work(pipefd[0]);
                exit(0);
            }
            // father -> write
            string cname = "channel-" + to_string(number);
            close(pipefd[0]);
            channels.push_back(Channel(pipefd[1], id, cname));

            // 把父进程的写端fd保存起来
            fds.push_back(pipefd[1]);
        }
        return 0;
    }
    // 轮流均衡地依次选择子进程对应的管道：0，1，2...n - 1(每个子进程及管道的编号实际上就是数组的下标)
    int NextChannel()
    {
        static int next = 0;
        int c = next;
        next++;
        next %= channels.size();
        return c;
    }
    // 父进程发送任务码
    void SendTaskCode(int index, uint32_t code)
    {
        cout << "send code: " << code << " to " << channels[index].name() << " sub prorcess id: " <<  channels[index].pid() << endl;
        write(channels[index].wfd(), &code, sizeof(code));
    }

    // 退出全部的子进程（只需要关闭对应信道管道Channel中的写端即可）并回收子进程的资源
    void KillAll()
    {
        for (auto &channel : channels)
        {
            //关闭管道写端文件描述符，子进程work中的read读到文件末尾（0）后将exit(0)退出
            channel.Close();
            //父进程阻塞等待子进程，回收所有已退出子进程的资源
            pid_t pid = channel.pid();
            pid_t rid = waitpid(pid, nullptr, 0);
            if (rid == pid)
            {
                cout << "wait sub process: " << pid << " success..." << endl;
            }
            cout << channel.name() << " close done" << " sub process quit now : " << channel.pid() << endl;
        }
    }

    void Debug()
    {
        for (auto &channel : channels)
        {
            channel.PrintDebug();
        }
    }
    ~ProcessPool()
    { }

private:
    int _sub_process_num;
    vector<Channel> channels;
};

//控制进程池
void CtrlProcessPool(ProcessPool *processpool_ptr, int cnt)
{
    while (cnt)
    {
        // a. 选择一个进程和通道
        int channel = processpool_ptr->NextChannel();

        // b. 你要选择一个任务
        uint32_t code = NextTask();

        // c. 发送任务
        processpool_ptr->SendTaskCode(channel, code);

        sleep(1);
        cnt--;
    }
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return UsageError;
    }
    int sub_process_num = stoi(argv[1]);
    if (sub_process_num <= 0) return ArgError;

    srand((uint64_t)time(nullptr));

    // 1. 创建通信信道和子进程
    ProcessPool *processpool_ptr = new ProcessPool(sub_process_num);
    processpool_ptr->CreateProcess(worker);
    processpool_ptr->Debug();

    // 2. 控制子进程
    CtrlProcessPool(processpool_ptr, 10); //设置发送10个任务

    cout << "task run done" << endl; //任务全部执行完毕的提示符

    // 3. 回收子进程要进行两步
    // 首先，让所有的子进程退出；然后，父进程回收已退出子进程的资源（wait/waitpid）
    processpool_ptr->KillAll();

    delete processpool_ptr;
    return 0;
}
